#include "messages_channels.hpp"

#include "messages_channels_container.hpp"
#include "channels_container.hpp"

#include <iostream>
namespace fasys{ namespace channels{


time_t s_now = 0;

/*
struct f_query
{
  message_list& ml;
  time_t now;
  f_query(message_list& ml, time_t now): ml(ml), now(now){}
  void operator()(const message_info& mi)
  {
    ml.messages.push_back( basic_message() );
    basic_message& bm = ml.messages.back();
    bm.message_id = mi.message_id;
    bm.create_time = mi.create_time;
    bm.lifetime = mi.lifetime;
    bm.order = mi.order;
    //bm.publish_time = mi.publish_time;
    bm.death_time = mi.death_time;
    bm.death_timespan = bm.death_time > now ? bm.death_time - now : 0;
    bm.data = mi.data;
  }
};
*/

template<bool Detach = false >
struct f_message_list
{
  typedef message_list_response::message_list_type message_list_type;
  message_list_type& ml;
  f_message_list(message_list_type& ml): ml(ml){}
  void operator()(const message_info& mi)
  {
    ml.push_back( message_list_item() );
    message_list_item& bm = ml.back();
    bm.message_id = mi.message_id;
    bm.create_time = mi.create_time;
    bm.lifetime = mi.lifetime;
    bm.order = mi.order;
    bm.death_time = mi.death_time;
    bm.death_timespan = bm.death_time > s_now ? bm.death_time - s_now : 0;
    get_content(bm,mi);
  }
  void get_content(message_list_item& bm, const message_info& mi);
};

template<>
void f_message_list<false>::get_content(message_list_item& bm, const message_info& mi)
{
  bm.content = mi.data;
}

template<>
void f_message_list<true>::get_content(message_list_item& bm, const message_info& mi)
{
  const_cast<message_info&>(mi).data.swap(bm.content);
}

/*
struct f_message_list_detach
{
  typedef message_list_response::message_list_type message_list_type;
  message_list_type& ml;
  f_message_list_detach(message_list_type& ml): ml(ml){}
  void operator()(message_info& mi)
  {
    ml.push_back( message_list_item() );
    message_list_item& bm = ml.back();
    bm.message_id = mi.message_id;
    bm.create_time = mi.create_time;
    bm.lifetime = mi.lifetime;
    bm.order = mi.order;
    bm.death_time = mi.death_time;
    bm.death_timespan = bm.death_time > s_now ? bm.death_time - s_now : 0;
    bm.content.swap(mi.data);
  }
};*/

void operator << ( message_info& mi, const message_notify& m)
{
  mi.order = m.order;
  mi.message_id = m.message_id;
  mi.channel_id = m.channel_id;
  mi.order = m.order;
  mi.create_time = m.create_time;
  mi.lifetime = m.lifetime;
  mi.death_time = m.death_time;
  mi.limit = m.limit;
}

void operator << ( message_info& mi, const message_list_item& m)
{
  mi.order = m.order;
  mi.message_id = m.message_id;
//   mi.channel_id = m.channel_id;
  mi.order = m.order;
  mi.create_time = m.create_time;
  mi.lifetime = m.lifetime;
  mi.death_time = m.death_time;
  mi.limit = m.limit;
}


messages_channels::~messages_channels()
{
  delete _messages;
  if ( _owner_channels ) delete _channels;

}

messages_channels::messages_channels()
  : _messages(new messages_channels_container)
  , _channels(new channels_container)
  , _owner_channels(true)
{
}

messages_channels::messages_channels(channels_container* channels)
  : _messages(new messages_channels_container)
  , _channels(channels)
  , _owner_channels(false)
{
}

messages_channels::messages_channels( const messages_channels& mc)
{
  // TODO:
}

channel_id_t messages_channels::create(const std::string& channel, size_t min_size, size_t max_size)
{
  s_now = time(0);
  channel_info ci = _channels->create( channel );
  ci.update_time = s_now;
  ci.min_size = min_size;
  ci.max_size = max_size;
  _channels->update(ci);
  return ci.channel_id;
}

bool messages_channels::publish(message_publish& mp, message_notify& mn)
{
  s_now = time(0);

  static_cast<message_publish&>(mn) = mp;

  if ( mn.create_time == 0 )
    mn.create_time = s_now;

  if ( mn.lifetime == 0 )
    mn.lifetime = FASYS_DEFAULT_TIMELIFE;

  mn.death_time = s_now + mn.lifetime;
  mn.death_timespan = mn.lifetime;

  mn.channel.swap(mp.channel);

  //2. Создание канала
  channel_info ci = _channels->create( mn.channel );
  ci.update_time = s_now;

  mn.channel_id = ci.channel_id;
  mn.message_id = ++ci.message_id_counter;

  if ( mn.limit < ci.min_size )
    mn.limit = ci.min_size;

  if ( mn.limit > ci.max_size )
    mn.limit = ci.max_size;

  message_info mi;
  mi.data.swap(mp.content);
  mi << mn;
  mn.count = _messages->insert(mi);
  _channels->update(ci);
  mn.content.swap(mi.data);
  std::cout << "  >> " << s_now << "<=" << ci.subscribe_time << " id="<< ci.channel_id << " name=" << ci.name << std::endl;
  return s_now <= ci.subscribe_time;

  /*
  //1. Первичная обработка
  mn.create_time = m.create_time == 0 ? now : m.create_time;

  if ( m.lifetime == 0 )
    m.lifetime = FASYS_DEFAULT_TIMELIFE;

  m.death_time = m.publish_time + m.lifetime;
  m.death_timespan = m.lifetime;

  //2. Создание канала
  channel_info ci = _channels->create( m.channel_name );
  ci.update_time = now;
  //3. Вторичная обработка
  m.channel_id = ci.channel_id;
  m.message_id = ++ci.message_id_counter;
  //4. Добавление сообщения в канал
  message_info mi;
  mi.data.swap(m.data);
  mi << m;
  m.count = _messages->insert(mi);
  //5. Обновление канала
  _channels->update(ci);
  //6. Установка выходных данных
  m.data.swap(mi.data);
  return now <= ci.subscribe_time;
  */
}

// Обновляем ранее опубликованное сообщение 
bool messages_channels::update(const message_notify& mn)
{
  s_now = time(0);

  channel_info ci = _channels->create( mn.channel );
  ci.update_time = s_now;

  message_info mi;
  mi << mn;
  mi.lifetime = mn.death_timespan;
  mi.death_time = s_now + mi.lifetime;
  // Заменяем channel_id (если пришел из другого контейнера то он не правильный)
  mi.channel_id = ci.channel_id;
  mi.data = mn.content;

  if (  mi.limit < ci.min_size )  mi.limit = ci.min_size;
  if (  mi.limit > ci.max_size )  mi.limit = ci.max_size;

  _messages->insert_or_replace(mi);
  _channels->update(ci);
  return s_now <= ci.subscribe_time;
}

bool messages_channels::update(const message_list_response& mlr)
{
  s_now = time(0);
  typedef message_list_response::message_list_type message_list_type;
  message_list_type::const_iterator beg = mlr.message_list.begin();
  message_list_type::const_iterator end = mlr.message_list.end();

  channel_info ci = _channels->create( mlr.channel );
  ci.update_time = s_now;
  
  message_info mi;
  for ( ;beg!=end; ++beg)
  {
    mi << *beg;
    if (  mi.limit < ci.min_size )  mi.limit = ci.min_size;
    if (  mi.limit > ci.max_size )  mi.limit = ci.max_size;
    mi.data = beg->content;
    mi.channel_id = ci.channel_id;
    mi.lifetime = beg->death_timespan;
    mi.death_time = s_now + mi.lifetime;
    _messages->insert_or_replace(mi);
  }
  _channels->update(ci);
  return s_now <= ci.subscribe_time;
}



// Просто помечаем что есть подписчики 
channel_id_t messages_channels::subscribe( const std::string& name, time_t timeout )
{
  time_t now = time(0);
  channel_info ci = _channels->create( name );
  ci.update_time = now;
  ci.subscribe_time = now + timeout;
  _channels->update(ci);
  return ci.channel_id;
}

// Просто помечаем что есть подписчики 
bool messages_channels::subscribe( subscribe_request& srq, subscribe_response& srs )
{
  std::cout << "bool messages_channels::subscribe( subscribe_request& srq, subscribe_response& srs )" << srq.channel << std::endl;
  
  s_now = time(0);
  srs.channel.swap(srq.channel);
  time_t subscribe_time = s_now + srq.timeout;
  channel_info ci = _channels->create( srs.channel );
  if ( subscribe_time > ci.subscribe_time )
  {
    ci.update_time = s_now;
    ci.subscribe_time = subscribe_time;
    std::cout << "subscribe_time > ci.subscribe_time = " << ci.subscribe_time << " id="<< ci.channel_id << " name=" << ci.name << std::endl;
    _channels->update(ci);
    srs.timeout = srq.timeout;
  }
  else
    srs.timeout = ci.subscribe_time - s_now;
  srs.channel_id = ci.channel_id;
  srs.count = _messages->channel_size( ci.channel_id );
  return true;
}

/*
void messages_channels::query(message_list& ml)
{
  if ( channel_info ci = _channels->find( ml.channel_name ) )
  {
    time_t now = time(0);
    ml.channel_id = ci.channel_id;
    ml.count = _messages->select_by_order(f_query(ml, now), ci.channel_id, 0, ml.limit );
  }
}
*/

void messages_channels::query( message_list_request& mlrq, message_list_response& mlrs) const
{
  s_now = time(0);
  mlrs.channel.swap(mlrq.channel);
  mlrs.limit = mlrq.limit;
  mlrs.offset = mlrq.offset;
  if ( channel_info ci = _channels->find( mlrs.channel ) )
  {
    mlrs.channel_id = ci.channel_id;
    mlrs.count = _messages->select_by_order(f_message_list<false>(mlrs.message_list), mlrs.channel_id, mlrs.offset, mlrs.limit );
  }
}

void messages_channels::detach( message_list_request& mlrq, message_list_response& mlrs)
{
  s_now = time(0);
  mlrs.limit = mlrq.limit;
  mlrs.offset = mlrq.offset;
  if ( channel_info ci = _channels->find( mlrs.channel ) )
  {
    mlrs.channel_id = ci.channel_id;
    mlrs.count = _messages->select_by_order(f_message_list<true>(mlrs.message_list), mlrs.channel_id, mlrs.offset, mlrs.limit );
  }
}
/*
bool messages_channels::detach_first( message_list_response& mlrs)
{
  std::pair<channel_id, size_t> result
      = _messages->detach_first_by_order(f_message_list<true>(mlrs.message_list), mlrs.offset, mlrs.limit );

  
}
*/


}}